#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.h"
#include "pw_pbl/VSTOPtr.hpp"

struct AA
{
	uint32_t a;
	operator uint32_t(void) const {return a;}
	AA(uint32_t v = 0) : a(v){}
};

TEST_CASE("VSTORef"){
	uint8_t a = 0x12;
	pw_pbl::VSTORef<uint32_t> ref = a;
	CHECK_FALSE(!ref);
	CHECK(~ref == uint32_t(~0x12));
	CHECK(ref + 3 == 0x12 + 3);
	CHECK(ref - 2 == 0x12 - 2);
	CHECK(ref * 5 == 0x12 * 5);
	CHECK(ref / 6 == 0x12 / 6);
	CHECK(ref % 7 == 0x12 % 7);
	CHECK((ref & 8) == (0x12 & 8));
	CHECK((ref | 9) == (0x12 | 9));
	CHECK((ref ^ 0xA) == (0x12 ^ 0xA));
	auto kk = AA(0x5678);
	auto* mm = &kk;
	pw_pbl::VSTORef m = mm;
	CHECK(m->a == 0x5678);
}

TEST_CASE("VSTOPtr"){
	SUBCASE("uint32_t"){
		pw_pbl::VSTOPtr<uint32_t> ptr;
		REQUIRE_FALSE(ptr);
		uint8_t a[3] = {0, 0, 0};
		ptr = &a[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0);
		*ptr = 0x1234;
		CHECK(*ptr == 0x34);
		CHECK(a[0] == 0);
		CHECK(a[1] == 0x34);
		CHECK(a[2] == 0);
		a[0] = 0x12;
		a[2] = 0x56;
		CHECK(ptr[+1] == 0x56);
		CHECK(ptr[ 0] == 0x34);
		CHECK(ptr[-1] == 0x12);
		CHECK(*++ptr == 0x56);
		CHECK(*--ptr == 0x34);
		CHECK(*ptr-- == 0x34);
		CHECK(*ptr++ == 0x12);
		CHECK(*ptr == 0x34);
		uint16_t b[3] = {0, 0, 0};
		ptr = &b[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0);
		*ptr = 0x12345678;
		CHECK(*ptr == 0x5678);
		CHECK(b[0] == 0);
		CHECK(b[1] == 0x5678);
		CHECK(b[2] == 0);
		b[0] = 0x1234;
		b[2] = 0xABCD;
		CHECK(ptr[+1] == 0xABCD);
		CHECK(ptr[ 0] == 0x5678);
		CHECK(ptr[-1] == 0x1234);
		CHECK(*++ptr == 0xABCD);
		CHECK(*--ptr == 0x5678);
		CHECK(*ptr-- == 0x5678);
		CHECK(*ptr++ == 0x1234);
		CHECK(*ptr == 0x5678);
		uint32_t c[3] = {0, 0, 0};
		ptr = &c[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0);
		*ptr = 0x12345678;
		CHECK(*ptr == 0x12345678);
		CHECK(c[0] == 0);
		CHECK(c[1] == 0x12345678);
		CHECK(c[2] == 0);
		c[0] = 0x55555555;
		c[2] = 0xAAAAAAAA;
		CHECK(ptr[+1] == 0xAAAAAAAA);
		CHECK(ptr[ 0] == 0x12345678);
		CHECK(ptr[-1] == 0x55555555);
		CHECK(*++ptr == 0xAAAAAAAA);
		CHECK(*--ptr == 0x12345678);
		CHECK(*ptr-- == 0x12345678);
		CHECK(*ptr++ == 0x55555555);
		CHECK(*ptr == 0x12345678);
		uint64_t d[3] = {0, 0, 0};
		ptr = &d[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0);
		*ptr = 0x87654321;
		CHECK(*ptr == 0x87654321);
		CHECK(d[0] == 0);
		CHECK(d[1] == 0x87654321);
		CHECK(d[2] == 0);
		d[0] = 0x13579BDF;
		d[2] = 0x2468ACE0;
		CHECK(ptr[+1] == 0x2468ACE0);
		CHECK(ptr[ 0] == 0x87654321);
		CHECK(ptr[-1] == 0x13579BDF);
		CHECK(*++ptr == 0x2468ACE0);
		CHECK(*--ptr == 0x87654321);
		CHECK(*ptr-- == 0x87654321);
		CHECK(*ptr++ == 0x13579BDF);
		CHECK(*ptr == 0x87654321);
		AA e[3];
		ptr = &e[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0);
		*ptr = 0x12345678;
		CHECK(*ptr == 0x12345678);
		CHECK(e[0] == 0);
		CHECK(e[1] == 0x12345678);
		CHECK(e[2] == 0);
		e[0] = 0x55555555;
		e[2] = 0xAAAAAAAA;
		CHECK(ptr[+1] == 0xAAAAAAAA);
		CHECK(ptr[ 0] == 0x12345678);
		CHECK(ptr[-1] == 0x55555555);
		CHECK(*++ptr == 0xAAAAAAAA);
		CHECK(*--ptr == 0x12345678);
		CHECK(*ptr-- == 0x12345678);
		CHECK(*ptr++ == 0x55555555);
		CHECK(*ptr == 0x12345678);
		ptr = nullptr;
		REQUIRE_FALSE(ptr);
	}
	SUBCASE("const uint32_t"){
		pw_pbl::VSTOPtr<const uint32_t> ptr;
		REQUIRE_FALSE(ptr);
		uint8_t a[3] = {0x12, 0x34, 0x56};
		ptr = &a[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0x34);
		CHECK(ptr[+1] == 0x56);
		CHECK(ptr[ 0] == 0x34);
		CHECK(ptr[-1] == 0x12);
		CHECK(*++ptr == 0x56);
		CHECK(*--ptr == 0x34);
		CHECK(*ptr-- == 0x34);
		CHECK(*ptr++ == 0x12);
		CHECK(*ptr == 0x34);
		uint16_t b[3] = {0x1234, 0x5678, 0xABCD};
		ptr = &b[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0x5678);
		CHECK(ptr[+1] == 0xABCD);
		CHECK(ptr[ 0] == 0x5678);
		CHECK(ptr[-1] == 0x1234);
		CHECK(*++ptr == 0xABCD);
		CHECK(*--ptr == 0x5678);
		CHECK(*ptr-- == 0x5678);
		CHECK(*ptr++ == 0x1234);
		CHECK(*ptr == 0x5678);
		uint32_t c[3] = {0x55555555, 0x12345678, 0xAAAAAAAA};
		ptr = &c[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0x12345678);
		CHECK(ptr[+1] == 0xAAAAAAAA);
		CHECK(ptr[ 0] == 0x12345678);
		CHECK(ptr[-1] == 0x55555555);
		CHECK(*++ptr == 0xAAAAAAAA);
		CHECK(*--ptr == 0x12345678);
		CHECK(*ptr-- == 0x12345678);
		CHECK(*ptr++ == 0x55555555);
		CHECK(*ptr == 0x12345678);
		uint64_t d[3] = {0x13579BDF, 0x87654321, 0x2468ACE0};
		ptr = &d[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0x87654321);
		CHECK(ptr[+1] == 0x2468ACE0);
		CHECK(ptr[ 0] == 0x87654321);
		CHECK(ptr[-1] == 0x13579BDF);
		CHECK(*++ptr == 0x2468ACE0);
		CHECK(*--ptr == 0x87654321);
		CHECK(*ptr-- == 0x87654321);
		CHECK(*ptr++ == 0x13579BDF);
		CHECK(*ptr == 0x87654321);
		AA e[3] = {0x55555555, 0x12345678, 0xAAAAAAAA};
		ptr = &e[1];
		REQUIRE(ptr);
		CHECK(*ptr == 0x12345678);
		CHECK(ptr[+1] == 0xAAAAAAAA);
		CHECK(ptr[ 0] == 0x12345678);
		CHECK(ptr[-1] == 0x55555555);
		CHECK(*++ptr == 0xAAAAAAAA);
		CHECK(*--ptr == 0x12345678);
		CHECK(*ptr-- == 0x12345678);
		CHECK(*ptr++ == 0x55555555);
		CHECK(*ptr == 0x12345678);
		ptr = nullptr;
		REQUIRE_FALSE(ptr);
	}
}

TEST_CASE("VSTORWPtrPair"){
	pw_pbl::VSTORWPtrPair<uint32_t> ptrp;
	REQUIRE_FALSE(ptrp.r());
	REQUIRE_FALSE(ptrp.w());
	SUBCASE("uint8_t"){
		uint8_t a0[5] = {0x12, 0x34, 0x56, 0x78, 0xAB};
		const uint8_t a1[5] = {0x21, 0x43, 0x65, 0x87, 0xBA};
		ptrp = {&a1[2], &a0[2]};
		REQUIRE(ptrp.r());
		REQUIRE(ptrp.w());
		CHECK(ptrp.r()[-2] == 0x21);
		CHECK(ptrp.r()[-1] == 0x43);
		CHECK(ptrp.r()[ 0] == 0x65);
		CHECK(ptrp.r()[+1] == 0x87);
		CHECK(ptrp.r()[+2] == 0xBA);
		CHECK(ptrp.w()[-2] == 0x12);
		CHECK(ptrp.w()[-1] == 0x34);
		CHECK(ptrp.w()[ 0] == 0x56);
		CHECK(ptrp.w()[+1] == 0x78);
		CHECK(ptrp.w()[+2] == 0xAB);
	}
	SUBCASE("uint16_t"){
		uint16_t a0[5] = {0x1234, 0x5678, 0x89AB, 0xCDEF, 0x1357};
		const uint16_t a1[5] = {0x4321, 0x8765, 0xBA98, 0xFEDC, 0x7531};
		ptrp = {&a1[2], &a0[2]};
		REQUIRE(ptrp.r());
		REQUIRE(ptrp.w());
		CHECK(ptrp.r()[-2] == 0x4321);
		CHECK(ptrp.r()[-1] == 0x8765);
		CHECK(ptrp.r()[ 0] == 0xBA98);
		CHECK(ptrp.r()[+1] == 0xFEDC);
		CHECK(ptrp.r()[+2] == 0x7531);
		CHECK(ptrp.w()[-2] == 0x1234);
		CHECK(ptrp.w()[-1] == 0x5678);
		CHECK(ptrp.w()[ 0] == 0x89AB);
		CHECK(ptrp.w()[+1] == 0xCDEF);
		CHECK(ptrp.w()[+2] == 0x1357);
	}
}

TEST_CASE("VSTORWRefPair"){
	SUBCASE("uint8_t"){
		uint8_t a0[5] = {0x12, 0x34, 0x56, 0x78, 0xAB};
		const uint8_t a1[5] = {0x21, 0x43, 0x65, 0x87, 0xBA};
		pw_pbl::VSTORWRefPair<uint32_t> refp = {a1[2], a0[2]};
		CHECK((&refp.r())[-2] == 0x21);
		CHECK((&refp.r())[-1] == 0x43);
		CHECK((&refp.r())[ 0] == 0x65);
		CHECK((&refp.r())[+1] == 0x87);
		CHECK((&refp.r())[+2] == 0xBA);
		CHECK((&refp.w())[-2] == 0x12);
		CHECK((&refp.w())[-1] == 0x34);
		CHECK((&refp.w())[ 0] == 0x56);
		CHECK((&refp.w())[+1] == 0x78);
		CHECK((&refp.w())[+2] == 0xAB);
	}
	SUBCASE("uint16_t"){
		uint16_t a0[5] = {0x1234, 0x5678, 0x89AB, 0xCDEF, 0x1357};
		const uint16_t a1[5] = {0x4321, 0x8765, 0xBA98, 0xFEDC, 0x7531};
		pw_pbl::VSTORWRefPair<uint32_t> refp = {a1[2], a0[2]};
		CHECK((&refp.r())[-2] == 0x4321);
		CHECK((&refp.r())[-1] == 0x8765);
		CHECK((&refp.r())[ 0] == 0xBA98);
		CHECK((&refp.r())[+1] == 0xFEDC);
		CHECK((&refp.r())[+2] == 0x7531);
		CHECK((&refp.w())[-2] == 0x1234);
		CHECK((&refp.w())[-1] == 0x5678);
		CHECK((&refp.w())[ 0] == 0x89AB);
		CHECK((&refp.w())[+1] == 0xCDEF);
		CHECK((&refp.w())[+2] == 0x1357);
	}
}
